import java.awt.Image;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;

import javax.media.opengl.GL;
import javax.media.opengl.glu.GLU;

import com.sun.opengl.util.texture.Texture;
import com.sun.opengl.util.texture.TextureData;
import com.sun.opengl.util.texture.TextureIO;

public class Clouds {
	GL gl;
	GLU glu;
	private int shaderprogram, cloud[];
	private int vert_shader, frag_shader;
	private float FPS;
	private int puff_num;
	public Vector3D center,spread, start;
	private Vector3D puff[];
	private int radius;
	Texture boxTexture1;
	
	public Clouds(GL gl_, GLU glu_){
		gl=gl_;
		glu=glu_;
		cloud = new int[1];
		init();
		loadShader();
		CreateTexture(null);
	}
	private float distance(Vector3D a, Vector3D b) {
		Vector3D c = new Vector3D();
		c.x = a.x - b.x;
		c.y = a.y - b.y;
		c.z = a.z - b.z;
	    return (float) Math.sqrt((c.x()*c.x() + c.y()*c.y() + c.z()*c.z()));
	}
	
	public Vector3D[] qSort(Vector3D[] A, int low, int high, Vector3D pos) {
	      int i = low;                
	      int j = high;
	      float x = distance(A[(low+high)/2],pos); 
	      do {
	          while(distance(A[i],pos) > x) ++i; 
	          while(distance(A[j],pos) < x) --j;  
	          if(i <= j){           	             
	              Vector3D temp = new Vector3D();//quick sort for sprites
	              temp = A[i];
	              A[i] = A[j];
	              A[j] = temp;	             
	              i++; j--;
	          }
	      } while(i < j);
	      if(low < j) A = qSort(A, low, j,pos);
	      if(i < high) A = qSort(A, i, high,pos);
	      return A;
	  }

	private void init(){
		puff_num = 70;//number of smoke clouds
		radius = 15;//sprite radius
		spread = new Vector3D();
		spread.setX(100f); //diameter of spread not recommended to touch!
		spread.setY(100f);
		spread.setZ(15f);
		center = new Vector3D();
		center.setX(-10f);
		if(MyJoglCanvas.player_field == 1) center.setY(150f);
		else center.setY(-10f);//lowest corner of spread NOT CENTER not recommended to touch!
		center.setZ(0f);
		start = new Vector3D();
		start.setX(center.x + spread.x/2f);
		start.setY(center.y + spread.y/2f);
		start.setZ(center.z + spread.z/2f);
		puff = new Vector3D[puff_num];
		clouds_init();
		FPS = 1;
	}
	public static float rnd() {
		return (float)Math.random();
	}
	private void clouds_init(){
	    for(int i = 0; i < puff_num; i++) {
	        Vector3D data = new Vector3D();
	        data.setX(center.x()+rnd()*spread.x());
	        data.setY(center.y()+rnd()*spread.y());
	        data.setZ(center.z()+rnd()*spread.z());
	        puff[i] = new Vector3D();
	        puff[i].add(data);
	    }
	}
	private void CreateTexture(Image image)
	 {
		 URL imageURL= this.getClass().getResource(
				 "cloud.png");
	        
		 TextureData data=null;
		 try {
			 data = TextureIO.newTextureData(
					 imageURL.openStream(),false, "png");
			 boxTexture1 = TextureIO.newTexture(data);
		 } catch (IOException exc) {
			 exc.printStackTrace();
			 System.exit(1);
		 }       
	        
		 gl.glEnable(GL.GL_TEXTURE_2D);
		 
		 gl.glGenTextures(1,cloud, 0);
		 gl.glBindTexture(GL.GL_TEXTURE_2D, cloud[0]);
		 gl.glTexImage2D(GL.GL_TEXTURE_2D, 0,GL.GL_RGBA,data.getWidth(),data.getHeight(),0,GL.GL_RGBA,
				 GL.GL_UNSIGNED_BYTE,data.getBuffer());
//		 gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,GL.GL_LINEAR);
//		 gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER,GL.GL_LINEAR);
	 }
	
	 private void loadShader(){
		 vert_shader = gl.glCreateShader(GL.GL_VERTEX_SHADER);
	        frag_shader = gl.glCreateShader(GL.GL_FRAGMENT_SHADER);
	        
	        BufferedReader brv = null;
			try {
				brv = new BufferedReader(
						new FileReader(
								"src/cloud.vert"));
			} catch (FileNotFoundException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
	        String vsrc = "";
	        String line;
	        try {
				while ((line=brv.readLine()) != null) {
				  vsrc += line + "\n";
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	        gl.glShaderSource(vert_shader, 1,
	        		new String[] {vsrc}, (int[])null, 0);
	        gl.glCompileShader(vert_shader);
	        
	        BufferedReader brf = null;
			try {
				brf = new BufferedReader(
						new FileReader(
								"src/cloud.frag"));
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	        String fsrc = "";
	        String lines;
	        try {
				while ((lines=brf.readLine()) != null) {
				  fsrc += lines + "\n";
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	        gl.glShaderSource(frag_shader, 1,
	        		new String[] {fsrc}, (int[])null, 0);
	        gl.glCompileShader(frag_shader);
	        
	        shaderprogram = gl.glCreateProgram();
	        gl.glAttachShader(shaderprogram, vert_shader);
	        gl.glAttachShader(shaderprogram, frag_shader);
	        gl.glLinkProgram(shaderprogram);
	        gl.glValidateProgram(shaderprogram);
	 }
	 private void setShader(){
		 	gl.glActiveTexture(GL.GL_TEXTURE2);
		    gl.glBindTexture(GL.GL_TEXTURE_2D,cloud[0] - 1);
	        gl.glUniform1i(gl.glGetUniformLocation(
	        		shaderprogram, "cloud"), 2);
	        gl.glUniform3f(gl.glGetUniformLocation(
	        		shaderprogram, "center"), center.x() + spread.x()/2f
	        		, center.y() + spread.y()/2f, center.z() + spread.z()/2f);
	 }
	 public void draw(float yaw, float pitch, int reflect) {
		 		FPS = MyJoglCanvas.fps();
			    Vector3D point1 = new Vector3D(0,-radius,-radius);
			    Vector3D point2 = new Vector3D(0,radius,-radius);
			    Vector3D point3 = new Vector3D(0,radius,radius);
			    Vector3D point4 = new Vector3D(0,-radius,radius);
			    if(reflect == 0) pitch = -pitch;

			    Matrix4x4 maty = new Matrix4x4();
			    Matrix4x4 matz = new Matrix4x4();
			    yaw = -yaw;   			    
			    maty = Matrix4x4.set((float)Math.cos(pitch),0,(float)Math.sin(pitch),0,
		                   0,1,0,0,
		                   (float)-Math.sin(pitch),0,(float)Math.cos(pitch),0,
		                   0,0,0,1);
		    

			    matz = Matrix4x4.set((float)Math.cos(yaw+Atmosphere.pi),(float)-Math.sin(yaw+Atmosphere.pi),0,0,
		    		(float)Math.sin(yaw+Atmosphere.pi),(float)Math.cos(yaw+Atmosphere.pi),0,0,
		                   0,0,1,0,
		                   0,0,0,1);
			    
			    point1 = Matrix4x4.mult(Matrix4x4.mult(matz,maty),point1);
			    point2 = Matrix4x4.mult(Matrix4x4.mult(matz,maty),point2);
			    point3 = Matrix4x4.mult(Matrix4x4.mult(matz,maty),point3);
			    point4 = Matrix4x4.mult(Matrix4x4.mult(matz,maty),point4);
			    gl.glUseProgram(shaderprogram);
			    setShader();
			    gl.glEnable(GL.GL_ALPHA_TEST);
			    gl.glEnable(GL.GL_BLEND);
			    gl.glBlendFunc(GL.GL_SRC_ALPHA,GL.GL_ONE_MINUS_SRC_ALPHA);
			    gl.glDepthMask(false);
			    gl.glDisable(GL.GL_CULL_FACE);
			    gl.glBegin(GL.GL_QUADS);
			    for(int i = 0; i< puff_num; i++) {
			    	gl.glTexCoord2d(0,0);
			    	gl.glVertex3f(point1.x()+puff[i].x(),point1.y()+puff[i].y(),point1.z()+puff[i].z());

			    	gl.glTexCoord2d(1,0);
			    	gl.glVertex3f(point2.x()+puff[i].x(),point2.y()+puff[i].y(),point2.z()+puff[i].z());

			    	gl.glTexCoord2d(1,1);
			    	gl.glVertex3f(point3.x()+puff[i].x(),point3.y()+puff[i].y(),point3.z()+puff[i].z());

			    	gl.glTexCoord2d(0,1);
			    	gl.glVertex3f(point4.x()+puff[i].x(),point4.y()+puff[i].y(),point4.z()+puff[i].z());
			    }
			    gl.glEnd();
			    gl.glDepthMask(true);
			    gl.glEnable(GL.GL_CULL_FACE);
			    gl.glDisable(GL.GL_ALPHA_TEST);
			    gl.glDisable(GL.GL_BLEND);
			    gl.glUseProgram(0);
	}
}